﻿using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using LoungeRepo.Contracts.Core;
using LoungeRepo.Contracts.EntityStore;
using LoungeRepo.Core;
using LoungeRepo.Extensions;
using NUnit.Framework;

namespace Test.LoungeRepo.Core
{
    [TestFixture]
    public partial class testLoungeRepository
    {
        class SimpleEntity : ILoungeRepoEntityIdentity
        {
            public string Id { get; set; }
            public string Partition { get; set; }

            public SimpleEntity Child1 { get; set; }
            public SimpleEntity Child2 { get; set; }
        }


        class ConventionalEntity
        {
            public string Id { get; set; }

            public string Text;
        }


        [Test]
        public void Store_string_as_entity()
        {
            const string ID = "mystring";

            string objectFilename = string.Format(@"{0}\{1}\{2}.entity",
                                            LoungeRepoConfig.DEFAULT_ROOT_FOLDER_PATH,
                                            "",
                                            ID);
            try { File.Delete(objectFilename); } catch{}

            using (var sut = new LoungeRepository())
            {
                sut.Store("hello, world!", ID);

                Assert.IsTrue(File.Exists(objectFilename));
                Assert.IsTrue(new FileInfo(objectFilename).Length > 0);
            }
        }


        [Test]
        public void Load_string_as_entity()
        {
            const string ID = "mystring";
            const string TEXT = "hello, world!";

            using (var sut = new LoungeRepository())
            {
                sut.Store(TEXT, ID);
            }

            using(var sut = new LoungeRepository())
            {
                Assert.AreEqual(TEXT, sut.Load<string>(ID));                
            }
        }


        [Test]
        public void Roundtrip_an_entity_hierarchy()
        {
            try { Directory.Delete(LoungeRepoConfig.DEFAULT_ROOT_FOLDER_PATH, true); }
            catch { }

            var eChild = new SimpleEntity { Id = "2", Partition = "c" };
            var eParent = new SimpleEntity { Id = "1", Partition = "p", Child1=eChild };

            using (var sut = new LoungeRepository())
            {
                sut.Store(eParent);
            }

            using (var sut = new LoungeRepository())
            {
                var eParent2 = sut.Load<SimpleEntity>("1", "p");
                Assert.AreEqual(eParent.Id, eParent2.Id);
                Assert.AreEqual(eChild.Id, eParent2.Child1.Id);
            }
        }


        [Test]
        public void Roundtrip_an_entity_dict_and_delete_it()
        {
            var dict = new Dictionary<string, SimpleEntity>();
            dict.Add("1", new SimpleEntity { Id = "1", Partition = "simple" });
            dict.Add("2", new SimpleEntity { Id = "2", Partition = "simple" });

            using(var sut = new LoungeRepository())
            {
                sut.Store(dict, "dict");

                var e = sut.Load<SimpleEntity>("1", "simple");
                Assert.AreEqual("1", e.Id);

                var dict2 = sut.Load<Dictionary<string, SimpleEntity>>("dict");
                e = dict2["2"];
                Assert.AreEqual("2", e.Id);

                sut.Delete("dict");
                try
                {
                    sut.Load<Dictionary<string, SimpleEntity>>("dict");
                    Assert.Fail();
                }
                catch(EntityStoreException){}
            }
        }

        
        [Test]
        public void Entities_are_delivered_from_cache()
        {
            using(var sut = new LoungeRepository())
            {
                var e = new SimpleEntity{ Id = "1", Partition = "simple" };
                sut.Store(e);

                var f = sut.Load<SimpleEntity>("1", "simple");
                Assert.AreSame(e, f);
            }
        }


        [Test]
        public void Nested_entities_are_delivered_from_cache()
        {
            using (var sut = new LoungeRepository())
            {
                var c = new SimpleEntity { Id = "2", Partition = "simple" };
                var e = new SimpleEntity
                {
                    Id = "1",
                    Partition = "simple",
                    Child1 = c
                };
                sut.Store(e);
            }

            using(var sut = new LoungeRepository())
            {
                var c = sut.Load<SimpleEntity>("2", "simple");
                var e = sut.Load<SimpleEntity>("1", "simple");
                Assert.AreSame(c, e.Child1);
            }
        }


        [Test]
        public void Nested_entities_are_added_to_cache_if_root_is_stored()
        {
            using(var sut = new LoungeRepository())
            {
                var c = new SimpleEntity { Id = "2", Partition = "simple" };
                var e = new SimpleEntity
                {
                    Id = "1",
                    Partition = "simple",
                    Child1 = c
                };
                sut.Store(e);

                Assert.AreSame(c, sut.Load<SimpleEntity>("2", "simple"));
            }
        }


        [Test]
        public void Nested_entities_are_added_to_cache_if_root_is_loaded()
        {
            using (var sut = new LoungeRepository())
            {
                var c = new SimpleEntity { Id = "2", Partition = "simple" };
                var e = new SimpleEntity
                {
                    Id = "1",
                    Partition = "simple",
                    Child1 = c
                };
                sut.Store(e);
            }

            using(var sut = new LoungeRepository())
            {
                var e = sut.Load<SimpleEntity>("1", "simple");
                var c = sut.Load<SimpleEntity>("2", "simple");

                Assert.AreSame(c, e.Child1);
            }
        }

        
        [Test]
        public void Select_entities_from_extent()
        {
            using(var sut = new LoungeRepository())
            {
                sut.Store(new SimpleEntity {Id = "1", Partition = "simple"});
                sut.Store(new SimpleEntity { Id = "2", Partition = "complex" });
                sut.Store(new SimpleEntity { Id = "3", Partition = "simple" });

                var selected = sut.GetExtent<SimpleEntity>().Where(e => e.Partition == "simple");
                Assert.AreEqual(2, selected.Count());
            }
        }


        [Test]
        public void Check_root_folder_path()
        {
            using(var sut = new LoungeRepository())
            {
                Assert.AreEqual(Environment.CurrentDirectory + @"\" + LoungeRepoConfig.DEFAULT_ROOT_FOLDER_PATH, sut.RootFolderPath);
            }
        }


        [Test]
        public void Store_and_load_without_a_partition_name()
        {
            using(var sut = new LoungeRepository())
            {
                var ce = new ConventionalEntity {Id = "42", Text = "hello"};
                sut.Store(ce);
            }

            using (var sut = new LoungeRepository())
            {
                var ce = sut.Load<ConventionalEntity>("42");
            }
        }

        [Test, ExpectedException(typeof(ArgumentException))]
        public void Fails_when_storing_a_nonentity_object_without_id()
        {
            using(var sut = new LoungeRepository())
            {
                sut.Store("fail doing this");
            }
        }


        [Test]
        public void Creates_regex_pattern_from_partitionNamePattern()
        {
            Assert.AreEqual(@".*A.+B\\C.*", @"*A?B\C*".ToRegexPattern());
        }


        [Test]
        public void Roundtrips_entity_with_partition_store()
        {
            try { Directory.Delete(LoungeRepoConfig.DEFAULT_ROOT_FOLDER_PATH, true); }
            catch { }
            try { Directory.Delete("SpecialPartition", true); }
            catch { }

            var regularEntity = new SimpleEntity { Id = "2", Partition = "c" };
            var entityToBeMappedToSpecialPartitionFolder = new SimpleEntity { Id = "99", Partition = "mapthis" };

            using (var sut = new LoungeRepository())
            {
                sut.RegisterPartitionEntityStore("mapthis", "SpecialPartition");

                sut.Store(regularEntity);
                sut.Store(entityToBeMappedToSpecialPartitionFolder);
            }

            Assert.IsTrue(Directory.Exists(LoungeRepoConfig.DEFAULT_ROOT_FOLDER_PATH + "\\c"));
            Assert.IsTrue(Directory.Exists("SpecialPartition\\mapthis"));

            using (var sut = new LoungeRepository())
            {
                sut.RegisterPartitionEntityStore("mapthis", "SpecialPartition");

                var e = sut.Load<SimpleEntity>("2", "c");
                Assert.AreEqual(e.Id, regularEntity.Id);

                e = sut.Load<SimpleEntity>("99", "mapthis");
                Assert.AreEqual(e.Id, entityToBeMappedToSpecialPartitionFolder.Id);
            }
            
        }
    }
}
